/*
  <div id="app" style="color: red;font-size: 20px;">
    hello, {{ name }}
    <span class="text" style="color: green;">{{age}}</span>
  </div>
*/
// https://github.com/vuejs/vue/blob/dev/src/compiler/parser/html-parser.js

// 可以匹配 id="app" id='app' id=app
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/;
// 标签名 <my-label></my-label>
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`;
// 特殊标签名 <my:label></my:label>
const qnameCapture = `((?:${ncname}\\:)?${ncname})`;
// 起始标签 <div
const startTagOpen = new RegExp(`^<${qnameCapture}`);
// 单标签、闭合标签 > />
const startTagClose = /^\s*(\/?)>/;
// 结束标签 </div>
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`);

// 分析html式模板转AST树 每匹配一点内容就取出并删除重新赋值html字符串
// 一直到html模板删除完毕
function parseHtmlToAst(html) {
  // console.log(html);
  let text,
      root,
      currentParent,
      stack = [];

  while (html) { // 循环html字符串模板 分别取出标签、文本等内容
    let textEnd = html.indexOf('<');

    if (textEnd === 0) { // 如果是开始标签
      const startTagMatch = parseStartTag(); // 获取开始标签里面的内容对象
      // console.log(startTagMatch);
      if (startTagMatch) { // 如果存在就对里面的属性进行处理
        start(startTagMatch.tagName, startTagMatch.attrs);
        continue;
      }

      const endTagMatch = html.match(endTag);

      if (endTagMatch) { // 对结束标签进行处理
        // console.log(endTagMatch);
        advance(endTagMatch[0].length);
        end(endTagMatch[1]);
        continue;
      }
    }

    if (textEnd > 0) { // 如果开始标签到下一个开始标签之间有内容 那就是文本内容
      text = html.substring(0, textEnd);
    }

    if (text) {
      advance(text.length);
      chars(text);
    }
  }

  function parseStartTag() { // 分析标签里的内容
    const start = html.match(startTagOpen);

    let end, // 结束标识
        attr; // 属性标识

    // console.log(start);
    if (start) { // 如果标签存在就 拿到里面的内容，并取出(包装对象)
      const match = {
        tagName: start[1],
        attrs: []
      }
      advance(start[0].length); // 每次匹配完就删除
      // console.log(html);

      // 如果模板字符串的下一项匹配到的不是(>)闭合标签 , 而是(id="xxx")属性
      while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {
        // console.log(attr);
        match.attrs.push({
          name: attr[1],
          value: attr[3] || attr[4] || attr[5] // 单、双引号、不加引号
        });
        // console.log(match);
        advance(attr[0].length); // 每次匹配完就删除
      }
      
      if (end) { // 如果存在(>)闭合标签就结束 返回匹配出来的 match 对象
        // console.log(end);
        advance(end[0].length);
        return match;
      }
    }
  }

  function advance(n) {
    html = html.substring(n); // 截取字符串 从n位开始到结束
  }

  // 对开始、结束标签、文本进行处理 (组合AST树)
  function start(tagName, attrs) {
    // console.log('-------开始-------');
    // console.log(tagName, attrs);
    const element = createASTElement(tagName, attrs);
    // console.log(element);

    if (!root) { // 如果还没有根元素
      root = element;
    }

    currentParent = element; // 要知道谁是谁的父标签
    stack.push(element); // 把这标签内容存进 栈 里
  }
  
  function end(tagName) {
    // console.log('-------结束-------');
    // console.log(tagName);
    // stack: [div, span]
    const element = stack.pop(); // 先取最后一项(当前项) span
    currentParent = stack[stack.length - 1]; // 拿到前一项 div
    
    if (currentParent) { // 如果前一项存在就是当前项的父亲
      // span => parent => div
      element.parent = currentParent;
      // div => children => push => span
      currentParent.children.push(element);
    }
  }
  
  function chars(text) {
    // console.log('-------文本-------');
    // console.log(text);
    text = text.trim();

    if(text.length) {
      currentParent.children.push({
        type: 3,
        text
      })
    }
  }
  
  function createASTElement(tagName, attrs) { // 创建元素
    return {
      tag: tagName,
      type: 1,
      children: [],
      attrs,
      parent // 根据parent拉取结构
    }
  }

  return root;
}

export {
  parseHtmlToAst
}
